require 'rubygems'
require 'sequel'
require 'pp'

class Sequel::Database
  def object_graph(master, *relations)
    from(master).eager_load(*relations)
  end
end

class Sequel::Dataset
  def eager_load(*relations)
    opts = relations.extract_options!
    master = @opts[:from].first

    # alias all columns
    tables = [master] + relations
    columns = {}
    aliases, selects, plans, pkeys, t_counter = {}, [], [], {}, 1
    tables.each do |t|
      c_counter = 0
      plan = {}
      columns[t] = {}
      @db[t].columns.each do |c|
        a = :"t#{t_counter}_#{c_counter += 1}"
        if c == :id
          pkeys[t] = a
        end
        aliases[a] = c
        columns[t][a] = c
        plan[c] = a
        selects << :"#{t}__#{c}".as(a)
      end
      plans << plan
      t_counter += 1
    end

    ds = select(*selects)
    master_key = :"#{master}__#{opts.values.first}"
    foreign_key = opts.keys.first
    relations.each do |t|
      ds.left_outer_join!(t, foreign_key => master_key)
    end
    
    ds.prepare_graph_method(tables, columns, pkeys)

    ds
  end
  
  def prepare_graph_method(tables, columns, pkeys)
    @graph_tables = tables
    @graph_columns = columns
    @graph_pkeys = pkeys
  end
  
  def graph()
    items = Hash.new {|h, k| h[k] = {}}
    
    # iterate over records
    each do |r|
      @graph_tables.each do |t|
        id = r[@graph_pkeys[t]]
        unless items[t][id]
          rec = @graph_columns[t].inject({}) {|m, kv| m[kv[1]] = r[kv[0]]; m}
          items[t][id] = rec
        end
      end
    end
    
    master_items = items[@graph_tables.first]
    
    # resolve references
    items.each do |t, records|
      unless t == @graph_tables.first
        records.each do |k, r|
          if item = master_items[r[:node_id]]
            item[t] ||= []
            item[t] << r
          end
        end
      end
    end

    master_items
  end
end

def bb
  t1 = Time.now
  yield
  Time.now - t1
end

DB = Sequel("postgres://postgres:postgres@localhost:5432/reality_development")

ds = DB.object_graph(:nodes, :attributes, :node_id => :id)

# pp ds.graph

puts bb {ds.graph}
